#pragma once 

/* 实现项目中一些公共的工具类接口 */
//1. 生成一个唯一ID的接口
//2. 文件读写操作接口

#include <iostream>
#include <string>
#include <atomic>
#include <sstream>
#include <fstream>
#include <random>
#include <iomanip>
#include "logger.hpp"

namespace ChatSystem
{
    class Util
    {
    public:
        //生成UUID的接口---在同一台主机上重复率是非常低的，但是当数据量非常大的时候多台主机之间重复的概率还是比较高的
        //如果要保证不同主机之间UUID的重复概率非常低，可以给UUID加上Mac地址；此时生成的UUID就具备了一台主机的地址，也具备了一台主机内非常随机的标识
        static std::string uuid()
        {
            //生成一个由16位随机字符组成的字符串作为唯一ID
            //1. 生成6个0到255之间的随机数字(每个字节--转换16进制字符)---生成12位16进制字符
            std::random_device rd;  //实例化设备随机数对象，用于生成设备随机数；虽然设备随机数生成很慢，但是非常随机
            std::mt19937 generator(rd());   //以设备随机数为种子，实例化伪随机数对象；
            //generator生成的可能数字会很大，我们需要的数字不需要这么大，就需要限制生成的数字范围
            std::uniform_int_distribution<int> distribution(0, 255);    //限定数据范围
            std::stringstream ss;
            for(int i = 0; i < 6; i++)
            {
                if(i == 2) ss << "-";
                ss << std::setw(2) << std::setfill('0') << std::hex << distribution(generator);
            }
            ss << "-";  //为了可读性给12位之后加一个 - ;
            //2. 通过一个静态变量生成一个2字节的编号数字---生成4位16进制数字字符
            static std::atomic<short> idx(0);  //数字编号，设置为静态，保证每次调用uuid这个函数后idx还是上次的那个值；
            short tmp = idx.fetch_add(1);   //对idx + 1 ，返回值是 + 1 之前的那个值
            ss << std::setw(4) << std::setfill('0') << std::hex << tmp;
            return ss.str();
        }

        //生成4位验证码
        static std::string vcode()
        {
            //生成一个由4位随机数字字符组成的验证码
            std::random_device rd;  //实例化设备随机数对象，用于生成设备随机数；虽然设备随机数生成很慢，但是非常随机
            std::mt19937 generator(rd());   //以设备随机数为种子，实例化伪随机数对象；
            //generator生成的可能数字会很大，我们需要的数字不需要这么大，就需要限制生成的数字范围
            std::uniform_int_distribution<int> distribution(1000, 9999);    //限定数据范围
            return std::to_string(distribution(generator));
        }

        //读取文件
        static bool readFile(const std::string& filename, std::string& body)
        {
            //由于读取文件有可能失败，所以返回值为bool
            //实现读取一个文件的所有数据，放入body中
            // ifstream 和 ofstream是用RAII思想来管理文件的；
            std::ifstream ifs(filename, std::ios::binary | std::ios::in);
            if(ifs.is_open() == false)
            {
                LOG_ERROR("打开文件 {} 失败！", filename);
                return false;
            }
            //先跳转到文件末尾
            ifs.seekg(0, std::ios::end);
            //获取文件末尾到文件起始为止的偏移量
            size_t flen = ifs.tellg();
            //再跳转到文件起始位置，开始读取文件内容
            ifs.seekg(0, std::ios::beg);
            //开始读取文件内容
            body.resize(flen);  //调整body的大小，让其可以放下文件的所有内容
            ifs.read(&body[0], flen);
            if(ifs.good() == false)
            {
                LOG_ERROR("读取文件 {} 数据失败！", filename);
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
            
        }

        //写文件
        static bool writeFile(const std::string& filename, const std::string& body)
        {
            //实现将body中的数据，写入filename对应的文件中
            // ifstream 和 ofstream是用RAII思想来管理文件的；
            //由于读取文件有可能失败，所以返回值为bool
            //实现读取一个文件的所有数据，放入body中
            // ifstream 和 ofstream是用RAII思想来管理文件的；
            std::ofstream ofs(filename, std::ios::binary | std::ios::out | std::ios::trunc); // std::ios::trunc表示当文件中有数据时直接截断
            if(ofs.is_open() == false)
            {
                LOG_ERROR("打开文件 {} 失败！", filename);
                return false;
            }

            ofs.write(&body[0], body.size());
            if(ofs.good() == false)
            {
                LOG_ERROR("向文件 {} 写入数据失败！", filename);
                ofs.close();
                return false;
            }
            ofs.close();
            return true;

        }
    private:  
    };
};